package com.espirit.eap.util.tagAdaptor;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;

import com.espirit.eap.annotation.ModelDisplayProperty;
import com.espirit.eap.manager.EapImportService;
import com.espirit.eap.manager.I18nService;
import com.espirit.eap.pagelayout.Element;
import com.espirit.eap.pagelayout.GridSection;
import com.espirit.eap.pagelayout.Layout;
import com.espirit.eap.pagelayout.Model;
import com.espirit.eap.pagelayout.ModelField;
import com.espirit.eap.pagelayout.ModelService;
import com.espirit.eap.pagelayout.PageCache;
import com.espirit.eap.pagelayout.SectionElement;
import com.espirit.eap.pagelayout.TagInfo;
import com.espirit.eap.util.ChineseSpeller;
import com.espirit.eap.util.JSONUtil;
import com.espirit.eap.util.TaglibManager;
import com.googlecode.cswish.model.PropertyInfo;
import com.googlecode.cswish.struts.RestAction;
import com.googlecode.cswish.struts.hibernate.HibernateUtil;
import com.googlecode.cswish.struts.spring.BeanIntrospector;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.ClassUtil;

@Service
public class TagSectionElementAdaptor implements TagElementAdaptor<SectionElement> {
	
	private static final Log logger = LogFactory.getLog(TagSectionElementAdaptor.class);
	
	@Resource
	private BeanIntrospector beanIntrospector;
	
	@Resource
	private I18nService i18nService;
	
	@Resource
	private TagAdaptorUtil tagAdaptorUtil;
	
	@Resource
	private TaglibManager tagManager;
	
	@Resource
	private ModelService modelService;
	
	@Resource
	private JSONUtil jsonUtil;
	
	public TagSectionElementAdaptor() {
	}
	
	@Override
	public TagInfo getTagProperties(Layout root, SectionElement sectionElement, Map<String, Object> context) {
		TagInfo tagInfo = new TagInfo(true);
		Map<String, Object> properties = tagInfo.getTagProperties();
		String tagName = getTagName(sectionElement);
		if (tagName == null) {
			logger.error("Wrong tag, found the null tagName(id=" + sectionElement.getId() 
					+ ",name=" + sectionElement.getName() +"), use text to avoid exception page");
			tagName = "text";
		}
		if (TAG_CODE.equals(tagName)) {
			String nestedName = tagAdaptorUtil.getName(sectionElement);
			tagInfo.setTagName(TAG_CODE);
			properties.put("name", nestedName);
			String exProperty = sectionElement.getExProperty();
			int index = exProperty == null ? -1 : exProperty.indexOf(Element.EXPROP_KV_SEP);
			if (index > 0) {
				int begin = exProperty.indexOf(Element.EXPROP_KV_SEP, index);
				if (begin > 0) {
					properties.put("value", exProperty.substring(begin + 1));
				}
			}
			return tagInfo;
		}
		
		String nestedName = tagAdaptorUtil.getName(sectionElement);
		if (nestedName == null) {
			// treat this tag as invalid tag
			return null;
		}
		
		properties.put("name", nestedName);
		properties.put(NAME, tagAdaptorUtil.getName(sectionElement));
		Boolean hidden = sectionElement.getVisible() != null && !sectionElement.getVisible();
		properties.put("show", !hidden);
		properties.put("required", sectionElement.getRequired() != null && sectionElement.getRequired());
		// Use sectionElement's i18n
		String i18n = sectionElement.getI18n();
		if (i18n != null) {
			properties.put("label", i18n);
		}
		properties.put("width", sectionElement.getWidth());
		properties.put("textModel", sectionElement.getReadOnly() != null && sectionElement.getReadOnly() ? Boolean.TRUE : Boolean.FALSE);
		properties.put("positionCol", sectionElement.getPositionCol());
		
		////////////////////////// pageinfo & datagrid ////////////////////////
		Element parent = (Element) HibernateUtil.unproxy(sectionElement.getParent());
		if (parent != null && parent instanceof GridSection) {
			boolean isRead = false;
			GridSection gridSection = (GridSection)parent;
			if (gridSection.getReadOnly() != null) {
				isRead = gridSection.getReadOnly();
			}
			properties.put("textModel", isRead);
		}
		
		// TODO: add the prefix automatically?
		/*
		if ("css".equals(tagName)) {
			String formId = (String) context.get(FORM_ID);
			if (formId != null) {
				properties.put("prefix", '#' + formId);
			}
		}
		*/
		
		// extra properties
		Map<String, String> exProperties = sectionElement.getExProperties();
		if (exProperties != null) {
			properties.putAll(exProperties);
		}
		
		tagInfo.setTagName(tagName);
		return tagInfo;
	}
	
	@Override
	public String getTagName(SectionElement element) {
		String tagName = element.getTagName();
		return tagName;
	}
	
	@Override
	public SectionElement updateToElement(Map<String, String> tagProperties, SectionElement existed, Map<String, Object> context, PageCache pageCache) {
		String tagName = tagProperties.get("tagName");
		if (TAG_CODE.equals(tagName)) {
			String value = tagProperties.get("value");
			existed.setTagName("code");
			existed.addExProperty("value", (value == null ? "" : value));
			return existed;
		}
		
		boolean isModelProperty = tagAdaptorUtil.isModelFieldTag(tagName);
		Map<String, String> properties = new HashMap<String, String>(tagProperties);
		String propertyName = properties.get(NAME);
		// try to fix the name (TODO: fix it in the frontend?)
//		if (propertyName != null && propertyName.startsWith("model.")) {
//			propertyName = propertyName.substring(6);
//			properties.put(NAME, propertyName);
//		}
		
		boolean nameChanged = isModelProperty && !StringUtils.equals(propertyName, existed.getName());
		// 2. name changed, update the model field
		if (nameChanged) {
			existed.setNewElement(true);
		}
		
		// i18n
		boolean labelChanged = tagAdaptorUtil.updateLabel(properties, existed, pageCache);
		
		
		// change the property name according to the i18n automatically
		// rule 1: label changed
		// rule 2: don't change the property name
		// rule 3: current property name is not model property
		boolean enableChangeNameAuto = true;		// TODO: refresh the front page
		if (enableChangeNameAuto && existed.getNewElement() != null && existed.getNewElement()) {			// it's new field
			if (!nameChanged && labelChanged && existed.getI18n() != null) {
				if (isModelProperty) {
					String name = existed.getI18n();
					if (name != null && name.length() > 0) {
						String newPropertyName = ChineseSpeller.convert(name, true, false);
						if (newPropertyName.length() > 0) {
							existed.setName(newPropertyName);		// default field name is the locale name(such as Chinese name)
							properties.remove("name");				// skip the original name
						}
					}
				}
			}
		}
		
		// the other standard property
		Map<String, Object> defaultTagProperties = (Map<String, Object>) context.get(DEFAULT_TAG_PROP);
		tagAdaptorUtil.updateElement(properties, defaultTagProperties, existed);
				
		return existed;
	}

	@Override
	public SectionElement addElement(Element parent, String tagName, Map<String, Object> properties, PageCache pageCache) {
		Integer index = pageCache.increaseMaxId();
		Layout root = pageCache.getLayout();
		
		String fieldName = (String)properties.get(NAME);
		// try to fix the name (FIXME: fix it in the frontend?)
		if ("modelselect".equals(tagName) && !fieldName.endsWith(".id")) {
			fieldName += ".id";
			properties.put("name", fieldName);
		}
		if (fieldName != null && fieldName.startsWith("model.")) {
			fieldName = fieldName.substring(6);
			properties.put("name", fieldName);
		}
		
		boolean isNewName = false;
		if (fieldName == null || fieldName.length() == 0) {
			boolean hasShortName = tagAdaptorUtil.isModelFieldTag(tagName);
			if (hasShortName) {
				Model dbModel = modelService.getModel(pageCache.getModel().getName());
				fieldName = tagAdaptorUtil.createName(tagName, parent, pageCache.getLayout(), tagManager, dbModel);
				isNewName = true;
			}
		}
		SectionElement sectionElement = new SectionElement();
		sectionElement.setTagName(tagName);
		sectionElement.setNewElement(isNewName);
		tagAdaptorUtil.createSectionElement(root, parent, sectionElement);
		if (fieldName != null && fieldName.length() > 0) {
			sectionElement.setName(fieldName);
		} else {
			// default name uses the tagName
			sectionElement.setName(tagName);
		}
		
		// TODO: correct? add "modelselect" check?
		boolean isModelIdField = false;
		if (fieldName != null && fieldName.endsWith(".id")) {
			fieldName = fieldName.substring(0, fieldName.length() - 3);
			isModelIdField = true;
		}
		String fullNameNoModelPrefix = getFullNameNoModelPrefix(parent, fieldName);
		
		// FIXME: is there better way to resolve the default value?
		// init the default value
		// if (isAdd && "modelSelect"), enableFreeText = true
		if ("modelselect".equals(tagName)) {
			String method = pageCache.getInvoker().methodName;
			Map<String, String> exProperties = sectionElement.safeExProperties();
			
			if (RestAction.isAdd(method) || RestAction.isUpdate(method)) {
				exProperties.put("enableFreeText", "true");
			}
			
			exProperties.put("property", "id");
			exProperties.put("labelProperty", "name");
			String modelName = beanIntrospector.getModelName(pageCache.getModel().getName());
			
			PropertyInfo propertyInfo = beanIntrospector.getPropertyInfo(modelName, fullNameNoModelPrefix, true);
			if (propertyInfo != null) {
				Class clazz = ClassUtil.getClass(propertyInfo.getGenericType());
				if (clazz != null) {
					ModelDisplayProperty modelDisplayProperty = (ModelDisplayProperty) clazz.getAnnotation(ModelDisplayProperty.class);
					if (modelDisplayProperty != null) {
						exProperties.put("labelProperty", modelDisplayProperty.value());
					}
				}
				exProperties.put("model", propertyInfo.getGenericType());
			}
			sectionElement.setExProperty(exProperties);
			
			// TODO: 
//			String newName = sectionElement.getName() + '.' + exProperties.get("property");
//			sectionElement.setName(newName);
		} else if ("importsetting".equals(tagName)) {
			String simpleName = beanIntrospector.getSimpleName(pageCache.getInvoker().actionName);
			String modelName = beanIntrospector.getModelName(simpleName);
			EapImportService eapImportService = ObjectFactory.getInstance().getBean(EapImportService.class, false);
			List<String> propertyNames = eapImportService.getDefaultImportPropertyNames(modelName);
			String strPropertyName = jsonUtil.json(propertyNames);
			sectionElement.addExProperty("targetModelProperties", strPropertyName);
		}
		
		// build the i18n
		// try to find i18n from the existed model field name
		if (fieldName != null && fieldName.length() > 0) {
			int subFieldIndex = fullNameNoModelPrefix.lastIndexOf('.');
			String modelName = sectionElement.getExProperty("model");
			if (modelName == null && parent != null) {
				modelName = parent.getExProperty("model");
			}
			String realFieldName = fieldName;
			
			if (modelName == null) {
				if (subFieldIndex > 0) {
					PropertyInfo propertyInfo = beanIntrospector.getPropertyInfo(pageCache.getModel().getName(),
							fullNameNoModelPrefix.substring(0, subFieldIndex), true);
					if (propertyInfo != null) {
						modelName = beanIntrospector.getSimpleName(propertyInfo.getGenericType());
						realFieldName = fullNameNoModelPrefix.substring(subFieldIndex + 1);
					} else {
						modelName = pageCache.getModel().getName();
						realFieldName = fullNameNoModelPrefix.substring(subFieldIndex + 1);
					}
				} else {
					modelName = pageCache.getModel().getName();
					realFieldName = fullNameNoModelPrefix;
				}
			}
			ModelField existedModelField = null;
			Model model = modelService.getModel(modelName);
			String i18n = null;
			if (model != null && realFieldName.indexOf('.') == -1) {
				if (isModelIdField) {
					i18n = model.getI18n();
				} else {
					existedModelField = model.getModelField(realFieldName);
					
					if (existedModelField != null) {
						i18n = existedModelField.getI18n();
					}
				}
			} else {
				// try to get the property from current model field
				i18n = modelService.getModelFieldI18n(pageCache.getModel().getName(), realFieldName);
			}
			if (i18n == null || i18n.isEmpty()) {
				i18n = realFieldName;
			}
			sectionElement.setI18n(i18n);
		}
		
		// set the element position
		sectionElement.setId(-index);
		Integer positionCol = (Integer) properties.get(POSITION_COL);
		Integer positionRow = (Integer) properties.get(POSITION_ROW);
		sectionElement.setPositionCol(positionCol);
		sectionElement.setPositionRow(positionRow);
		return sectionElement;
	}
	
	private String getFullNameNoModelPrefix(Element parent,
			String fieldName) {
		String namePrefix = tagAdaptorUtil.getNamePrefix(parent);
		String fullName = tagAdaptorUtil.getFullName(namePrefix, fieldName);
		String fullNameNoModelPrefix;
		if (fullName != null) {
			if (fullName.startsWith("model.")) {
				fullNameNoModelPrefix = fullName.substring(6);
			} else if (fullName.startsWith("execute.")) {
				fullNameNoModelPrefix = fullName.substring(8);
			} else {
				fullNameNoModelPrefix = fullName;
			}
		} else {
			fullNameNoModelPrefix = null;
		}
		return fullNameNoModelPrefix;
	}

	@Override
	public void deleteElement(Element parent, SectionElement existed, PageCache pageCache) {
	}
};